﻿using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;

namespace DynamicGeometry
{
    public class Dragger : Behavior
    {
        public override string Name
        {
            get { return "Drag"; }
        }

        public override string HintText
        {
            get { return "Use this tool to drag points and figures."; }
        }

        public override Color ToolButtonColor
        {
            get { return Color.FromArgb(255, 220, 220, 220); }
        }

        private List<IMovable> moving = null;
        private IEnumerable<IFigure> toRecalculate = null;
        private Point offsetFromFigureLeftTopCorner;
        private Point oldCoordinates;

        protected override void MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            offsetFromFigureLeftTopCorner = Coordinates(e);
            oldCoordinates = offsetFromFigureLeftTopCorner;

            moving = new List<IMovable>();
            IEnumerable<IFigure> roots = null;

            IFigure found = Drawing.Figures.HitTest(offsetFromFigureLeftTopCorner);
            IEnumerable<IFigure> selection = null;

            if (IsCtrlPressed())
            {
                if (found != null)
                {
                    found.Selected = !found.Selected;
                }
                selection = Drawing.GetSelectedFigures();
            }
            else
            {
                Drawing.ClearSelectedFigures();
                if (found != null)
                {
                    found.Selected = true;
                    selection = found.AsEnumerable();
                }
            }

            Drawing.RaiseSelectionChanged(new Drawing.SelectionChangedEventArgs(Drawing.GetSelectedFigures()));

            IMovable oneMovable = found as IMovable;
            if (oneMovable != null)
            {
                if (oneMovable is IPoint)
                {
                    // when we drag a point, we want it to snap to the cursor
                    // so that the point center is directly under the tip of the mouse
                    offsetFromFigureLeftTopCorner = new Point();
                    oldCoordinates = oneMovable.Coordinates;
                }
                else
                {
                    // however when we drag other stuff (such as text labels)
                    // we want the mouse to always touch the part of the draggable
                    // where it first touched during MouseDown
                    // we don't want the draggable to "snap" to the cursor like points do
                    offsetFromFigureLeftTopCorner = offsetFromFigureLeftTopCorner.Minus(oneMovable.Coordinates);
                }
                moving.Add(oneMovable);
                roots = found.AsEnumerable();
            }
            else if (found != null)
            {
                roots = DependencyAlgorithms.FindRoots(f => f.Dependencies, found);
                if (roots.All(root => root is IMovable))
                {
                    moving.AddRange(roots.Cast<IMovable>());
                }
            }

            if (roots != null)
            {
                toRecalculate = DependencyAlgorithms.FindDescendants(f => f.Dependents, roots).Reverse();
            }
            else
            {
                toRecalculate = null;
            }

            if (moving.IsEmpty())
            {
                moving.Add(Drawing.CoordinateSystem);
                var allFigures = Drawing.Figures.GetAllFiguresRecursive();
                //roots = DependencyAlgorithms.FindRoots(f => f.Dependencies, allFigures);
                //moving.AddRange(roots.OfType<IMovable>());
                //roots = null;
                toRecalculate = null; // Figures;
            }
        }

        private static bool IsCtrlPressed()
        {
            return (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control;
        }

        protected override void MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
        {
            var currentCoordinates = Coordinates(e);
            if (!moving.IsEmpty())
            {
                var offset = currentCoordinates.Minus(oldCoordinates);
                MoveAction action = new MoveAction(Drawing, moving, offset, toRecalculate);
                Drawing.ActionManager.RecordAction(action);
            }
            oldCoordinates = Coordinates(e);
            if (moving != null && moving.Count == 1 && moving[0] is IPoint)
            {
                oldCoordinates = moving[0].Coordinates;
            }
        }

        protected override void MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            moving = null;
        }

        public override System.Windows.Controls.Panel CreateIcon()
        {
            Point[] points = 
            {
                new Point(10, 5),
                new Point(10, 21),
                new Point(14, 17),
                new Point(18, 25),
                new Point(19, 25),
                new Point(20, 24),
                new Point(17, 17),
                new Point(17, 16),
                new Point(21, 16),
                new Point(10, 5)
            };
            var builder = IconBuilder.BuildIcon();
            var polygon = builder.AddPolygon(
                    points.Select(p => new Point(p.X / 32, p.Y / 32)));
            polygon.Fill = new SolidColorBrush(Colors.White);
            polygon.Stroke = new SolidColorBrush(Colors.Black);
            return builder.Canvas;
        }
    }
}